package entity

import (
	"fmt"
	"sort"
	"sync"
	"time"
)

/**
* @author aFun
* @data 2025/4/16 20:05
* @email: afunaaa222@163.com / 1030935903
* @address: Guangdong University of Technology, China
 */
// ============================================= 压测相关 =============================================
type Metrics struct {
	Mu        sync.Mutex
	Count     int             // 总请求数
	Success   int             // 成功请求数
	Fail      int             // 失败请求数
	Latencies []time.Duration // 响应时间
	StartTime time.Time       // 用于计算 QPS
}

type ReportMetrics struct {
	QPS         float64
	Avg         float64
	P99         float64
	Success     int64
	SuccessRate float64
	Fail        int64
	FailRate    float64
}

func (r ReportMetrics) String() string {
	return fmt.Sprintf(
		"QPS: %.2f\nAvg: %.2f ms\nP99: %.2f ms\nSuccess: %d (%.2f%%)\nFail: %d (%.2f%%)",
		r.QPS,
		r.Avg,
		r.P99,
		r.Success,
		r.SuccessRate*100,
		r.Fail,
		r.FailRate*100,
	)
}

func (m *Metrics) Record(success bool, duration time.Duration) {
	m.Mu.Lock()
	defer m.Mu.Unlock()
	m.Count++
	if success {
		m.Success++
	} else {
		m.Fail++
	}
	m.Latencies = append(m.Latencies, duration)
}

func (m *Metrics) Report() (rm *ReportMetrics) {
	total := m.Count
	if total == 0 {
		fmt.Println("No requests yet.")
		return
	}

	// QPS
	elapsed := time.Since(m.StartTime).Seconds()
	qps := float64(total) / elapsed

	// 平均响应时间
	var totalLatency time.Duration
	for _, l := range m.Latencies {
		totalLatency += l
	}
	avg := totalLatency / time.Duration(len(m.Latencies))

	// P99 响应时间
	sorted := append([]time.Duration(nil), m.Latencies...)
	sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
	p99 := sorted[int(float64(len(sorted))*0.99)]

	// 成功率 / 失败率
	successRate := float64(m.Success) / float64(total) * 100
	failRate := float64(m.Fail) / float64(total) * 100

	rm = &ReportMetrics{
		QPS:         qps,
		Avg:         float64(avg.Milliseconds()) / 1000.0,
		P99:         float64(p99.Milliseconds()) / 1000.0,
		Success:     int64(m.Success),
		SuccessRate: successRate,
		Fail:        int64(m.Fail),
		FailRate:    failRate,
	}
	return rm
}
